package com.banciyuan.bcywebview.base.view.video;

import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.media.MediaCodec;
import android.net.Uri;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StyleRes;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.VideoView;

import com.banciyuan.bcywebview.R;
import com.banciyuan.bcywebview.base.view.video.cache.BcyCacheDataSourceFactory;
import com.banciyuan.bcywebview.utils.common.UIUtils;
import com.banciyuan.bcywebview.utils.file.ImageManager;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import com.google.android.exoplayer2.util.Util;

import java.io.File;
import java.util.List;

import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FILL;
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT;
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FIXED_HEIGHT;
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FIXED_WIDTH;

/**
 * Created by squall on 2017/5/27.
 */

public class ScaleVideoView extends FrameLayout {

    private Uri mUri;
    private SimpleExoPlayer mMediaPlayer = null;
    private ComponentListener componentListener;
    private BcyPlaybackControlView mMediaController;
    private boolean orientation;
    private SurfaceView surfaceView;
    private int resizeMode = RESIZE_MODE_FIT;
    private OnReady onReady;
    private boolean isInit;

    private float videoAspectRatio;
    private static final float MAX_ASPECT_RATIO_DEFORMATION_FRACTION = 0.01f;

    public ScaleVideoView(@NonNull Context context) {
        super(context);
        initVideoView();
    }

    public ScaleVideoView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initVideoView();

    }

    public ScaleVideoView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initVideoView();

    }

    public ScaleVideoView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initVideoView();
    }


    @Override
    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
        super.onInitializeAccessibilityEvent(event);
        event.setClassName(VideoView.class.getName());
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
        super.onInitializeAccessibilityNodeInfo(info);
        info.setClassName(VideoView.class.getName());
    }

    private void initVideoView() {
        setFocusable(true);
        setFocusableInTouchMode(true);
        requestFocus();
    }

    public void setVideoURI(Uri uri, OnReady onReady) {
        this.onReady = onReady;
        mUri = uri;
        openVideo();
        requestLayout();
        invalidate();
    }

    public void stopPlayback() {
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    public void pause() {
        if (mMediaPlayer != null) {
            mMediaPlayer.setPlayWhenReady(false);
        }
    }

    public void continuePlay() {
        if (mMediaPlayer != null) {
            mMediaPlayer.setPlayWhenReady(true);
        }
    }

    public boolean getPlay() {
        if (mMediaPlayer != null) {
            return mMediaPlayer.getPlayWhenReady();
        } else {
            return false;
        }
    }

    private void openVideo() {
        componentListener = new ComponentListener();
        BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
        TrackSelection.Factory videoTrackSelectionFactory =
                new AdaptiveTrackSelection.Factory(bandwidthMeter);
        TrackSelector trackSelector =
                new DefaultTrackSelector(videoTrackSelectionFactory);
        mMediaPlayer = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector);
        mMediaPlayer.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);
        if (surfaceView != null) {
            mMediaPlayer.setVideoSurfaceView(surfaceView);
        }
        mMediaPlayer.setVideoListener(componentListener);
        mMediaPlayer.setTextOutput(componentListener);
        mMediaPlayer.addListener(componentListener);
        DefaultHttpDataSourceFactory defaultDataSourceFactory = new DefaultHttpDataSourceFactory(Util.getUserAgent(getContext(), "android"), null);
        HttpDataSource.RequestProperties requestProperties = defaultDataSourceFactory.getDefaultRequestProperties();
        requestProperties.set("Referer", mUri.getHost());
        SimpleCache simpleCache = new SimpleCache(new File(ImageManager.getVideoFile(getContext())), new NoOpCacheEvictor());
        BcyCacheDataSourceFactory cacheDataSourceFactory = new BcyCacheDataSourceFactory(simpleCache, defaultDataSourceFactory);
        ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
        MediaSource videoSource = new ExtractorMediaSource(mUri,
                cacheDataSourceFactory, extractorsFactory, null, null);

        mMediaPlayer.prepare(videoSource);
    }

    public void setFullScreen() {
        mMediaController.hide();
        ((Activity) getContext()).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        orientation = true;
        requestLayout();
        mMediaController.changeFullScreen(R.layout.bcy_media_controller_lanscope);
        mMediaController.show();
    }

    public void setMiniScreen() {
        mMediaController.hide();
        ((Activity) getContext()).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        orientation = false;
        requestLayout();
        mMediaController.changeFullScreen(R.layout.bcy_media_controller);
        mMediaController.show();
    }

    public void setMediaController(BcyPlaybackControlView controller, String uid) {
        if (mMediaController != null) {
            mMediaController.hide();
        }
        mMediaController = controller;
        mMediaController.setPlayer(mMediaPlayer, uid);
        mMediaController.setOnFullScreen(new OnFullScreen() {
            @Override
            public boolean fullScreenAction() {
                if (!orientation) {
                    setFullScreen();
                } else {
                    setMiniScreen();
                }
                return orientation;
            }
        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            toggleMediaControlsVisiblity();
        }
        return true;
    }

    public void toggleMediaControlsVisiblity() {
        if (mMediaController != null) {
            if (mMediaController.isVisible()) {
                mMediaController.hide();
            } else {
                mMediaController.show();
            }
        }

    }

    public void setSurfaceView(SurfaceView surfaceView) {
        this.surfaceView = surfaceView;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (resizeMode == RESIZE_MODE_FILL || videoAspectRatio <= 0) {
            // Aspect ratio not set.
            return;
        }

        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        float viewAspectRatio = (float) width / height;
        float aspectDeformation = videoAspectRatio / viewAspectRatio - 1;
        if (Math.abs(aspectDeformation) <= MAX_ASPECT_RATIO_DEFORMATION_FRACTION) {
            if (!orientation) {
                height = height - UIUtils.dip2px(6, getContext());
            }
            super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
            return;
        }

        switch (resizeMode) {
            case RESIZE_MODE_FIXED_WIDTH:
                height = (int) (width / videoAspectRatio);
                break;
            case RESIZE_MODE_FIXED_HEIGHT:
                width = (int) (height * videoAspectRatio);
                break;
            default:
                if (aspectDeformation > 0) {
                    height = (int) (width / videoAspectRatio);
                } else {
                    width = (int) (height * videoAspectRatio);
                }
                break;
        }
        if (!orientation) {
            height = height - UIUtils.dip2px(6, getContext());
        }
        super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
    }


    public interface OnFullScreen {
        boolean fullScreenAction();
    }

    private final class ComponentListener implements SimpleExoPlayer.VideoListener,
            TextRenderer.Output, ExoPlayer.EventListener {

        // TextRenderer.Output implementation

        @Override
        public void onCues(List<Cue> cues) {

        }

        // SimpleExoPlayer.VideoListener implementation
        @Override
        public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
                                       float pixelWidthHeightRatio) {
            ScaleVideoView.this.videoAspectRatio = height == 0 ? 1 : (width * pixelWidthHeightRatio) / height;
            requestLayout();
        }

        @Override
        public void onRenderedFirstFrame() {

        }

        @Override
        public void onTracksChanged(TrackGroupArray tracks, TrackSelectionArray selections) {

        }

        // ExoPlayer.EventListener implementation

        @Override
        public void onLoadingChanged(boolean isLoading) {
            // Do nothing.
        }

        @Override
        public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
            if (playbackState == ExoPlayer.STATE_READY && !isInit) {
                onReady.ready();
                if (mMediaPlayer != null) {
                    mMediaPlayer.setPlayWhenReady(true);
                }
                isInit = true;
            }
            if (mMediaPlayer != null) {
                boolean is_play = mMediaPlayer.getPlayWhenReady();

                if (!is_play && ScaleVideoView.this.getKeepScreenOn()) {
                    ScaleVideoView.this.setKeepScreenOn(false);
                } else if (is_play && !ScaleVideoView.this.getKeepScreenOn()) {
                    ScaleVideoView.this.setKeepScreenOn(true);
                }
            }

        }

        @Override
        public void onPlayerError(ExoPlaybackException e) {
            // Do nothing.
        }

        @Override
        public void onPositionDiscontinuity() {
            // Do nothing.
        }

        @Override
        public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
            // Do nothing.
        }

        @Override
        public void onTimelineChanged(Timeline timeline, Object manifest) {
            // Do nothing.
        }
    }

    public interface OnReady {
        void ready();
    }


}
